﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;

using agree.schema;

using alib;
using alib.Array;
using alib.Concurrency;
using alib.Enumerable;
using alib.Collections;
using alib.Collections.ReadOnly;
using alib.Debugging;

namespace agree
{
	using agree.mrs;

	public class TfsBag : seq_grammar_base, IIdentList<TfsSlot>
	{
		public static TfsBag FromXmrs(IRtParent parent, Mrs xmrs, Grammar g)
		{
			var bag = new TfsBag(parent, xmrs, g);

#if UNIFIER
			Unification.MrsToBag(g.ftm.r_none, xmrs, bag);
#else
			var fxh = new _from_xmrs_helper(xmrs, bag);
			bag._set_slots(fxh.rgts);
#endif
			return bag;
		}

		public TfsBag(IRtParent parent, IIdentity prv, Grammar g)
			: base(parent, prv, g)
		{
			this.arr = Collection<TfsSlot>.None;
		}

		TfsSlot[] arr;
		public void _set_slots(TfsSlot[] arr) { this.arr = arr; }

#if DEBUG
		public Tfs tfs;
#endif

		public int Count { get { return arr.Length; } }

		public TfsSlot this[int index] { get { return arr[index]; } }

		public IEnumerator<TfsSlot> GetEnumerator() { return arr.Enumerator(); }

		IEnumerator IEnumerable.GetEnumerator() { return arr.Enumerator(); }

		public void CopyTo(Array array, int index) { arr.CopyTo(array, index); }

		bool ICollection.IsSynchronized { get { return false; } }
		Object ICollection.SyncRoot { get { return this; } }

#if !UNIFIER
		struct _from_xmrs_helper
		{
			public _from_xmrs_helper(Mrs mrs, TfsBag bag)
			{
				if (!mrs.Variables.Select(v => v.num).IsSequential())
					throw new Exception();

				this.mrs = mrs;
				this.g = bag.Grammar;
				this.next_mark = 1;
				this.next_coref_mark = -1;
				this.late = new RefList<arr_tfs_entry>(40);

				var rels = mrs.RELS.a;
				int c_rels = rels.Length;
				this.rgts = new TfsSlot[c_rels];

				int c_corefs = 0;

				Debug.Print("=== {0} ===", g.Name);

				var rgix1 = new int[c_rels];
				for (int i = 0; i < c_rels; i++)
					rgix1[i] = load_rel(rels[i]);

				//foreach (var qeq in mrs.HCONS)
				//	pmrs.hcons.Add(MakeQeq(qeq));

				// write tfs

				var tfs = new ArrayTfs(
								bag.parent,
								mrs,
								g.ftm.r_none,
								Tfs.Flags.MultiRoot,
								default(Edge),
								late.GetTrimmed(),
								c_corefs);

#if DEBUG
				bag.tfs = tfs;
#endif

				for (int i = 0; i < c_rels; i++)
					rgts[i] = new TfsSlot(tfs, rgix1[i]);
			}

			Grammar g;
			Mrs mrs;
			Isometrics im { get { return g.im; } }
			MrsManager mrsm { get { return g.mrsm; } }

			RefList<arr_tfs_entry> late;
			public TfsSlot[] rgts;
			int next_mark;
			int next_coref_mark;


			int load_rel(Relation rel)
			{
				var t = get_rel_type(rel);

				int ix0 = late.Count;
				int m = t.HasAppropriateFeatures ? next_mark++ : 0;

				int i_dst = late.AllocateNew();
				var arr = late.GetUntrimmed();
				arr[i_dst].FeatMark = FeatMark.RootNode;
				arr[i_dst].e = g.tu.CreateEdge(t, m, false);

				if (m != 0)
				{
					var exp = t.Expanded;

					if ((exp.flags & Tfs.Flags.RootCoverage) == 0)
						throw new Exception();

					int ce = exp.EdgeCount;
					for (int i_src = 0; i_src < ce; i_src++)
					{
						if (exp.entries[i_src].mark == 1)
						{
							var ef = exp.entries[i_src].e_FlagsId & Edge.Flag.IdResolve;

							int nm = (ef & Edge.Flag.EtmNonBareType) != 0 ? next_mark++ : 0;

							i_dst = late.AllocateNew();
							arr = late.GetUntrimmed();
							arr[i_dst].FeatMark = new FeatMark(exp.entries[i_src].i_feat, m);
							arr[i_dst].e = new Edge(ef, nm);
						}
					}
				}
				return ix0 + 1;
			}


			Type get_rel_type(Relation rel)
			{
				FeatMgr ftm = im.ftm;

				var rgfix = new int[rel.Count];
				int i = 0;
				for (; i < rgfix.Length; i++)
				{
					if ((rgfix[i] = ftm.GetFeatureIndex(rel[i].name)) == -1)
						throw new Exception(String.Format("MRS argument '{0}' is not valid for grammar '{1}'", rel[i].name, g.Name));
				}

				//if (!String.IsNullOrWhiteSpace(rel.PRED))
				//	alib.Array.arr.Append(ref rgfix, mrsm.ifeat_pred);
				//if (!String.IsNullOrWhiteSpace(rel.CARG))
				//	alib.Array.arr.Append(ref rgfix, mrsm.ifeat_carg);

				var t = im.GetMaximalTypeForFeatures(rgfix);
				if (t == null)
					throw new Exception(String.Format("Grammar '{0}' has no type which is compatible with MRS features: {{ {1} }}",
						ftm.Grammar.Name,
						rgfix.Select(fix => ftm[fix].FEATURE).StringJoin(", ")));

				//Debug.Print("{0} - {{ {1} }}", t.Name, rgfix.Select(fix => ftm[fix].FEATURE).StringJoin(", "));

				return t;
			}


			void foo()
			{
				//foreach (var v in mrs.Variables)
				//	var_dict[v] = MakeVar(v);

				//var p_hook = new hook(pmrs, "hook");
				//var mh = mrs.HOOK;
				//p_hook.SetFeatureValue(mrsm.LTOP, GetVarOrMakeVacuous(mh.LTOP, mrsm.LTOP));
				//p_hook.SetFeatureValue(mrsm.INDEX, GetVarOrMakeVacuous(mh.INDEX, mrsm.INDEX));

				//if (mh.XARG != null)
				//	p_hook.SetFeatureValue(mrsm.XARG, GetVarOrMakeVacuous(mh.XARG, mrsm.XARG));

				//pmrs.hook = p_hook;

				//foreach (var rel in mrs.RELS)
				//	pmrs.rels.Add(MakeRelation(rel));

				//foreach (var qeq in mrs.HCONS)
				//	pmrs.hcons.Add(MakeQeq(qeq));

				//FixSubstructureNodeTypes();

				//pmrs.EquateQeqScopes();
			}

		};
#endif
	};


#if UNIFIER
	public unsafe sealed partial class Unification : IDisposable
	{
		void mrs_to_bag(Mrs mrs, TfsBag bag)
		{
			if (!mrs.Variables.Select(v => v.num).IsSequential())
				throw new Exception();

			var mtb = new mrs_to_bag_helper(this, mrs);

			bag._set_slots(mtb.rgts);


			this.freeU();
		}

		struct mrs_to_bag_helper
		{
			Unification u;
			Slot*[] var_slots;
			RefList<arr_tfs_entry> late;
			public TfsSlot[] rgts;
			int next_mark;
			int next_coref_mark;
			//uint ugen, cgen;

			Isometrics im { get { return u.tu.im; } }
			MrsManager mrsm { get { return u.tu.mrsm; } }

			public mrs_to_bag_helper(Unification u, Mrs mrs)
			{
				this.u = u;
				//this.ugen = (uint)(u.gen + 6);
				//this.cgen = (uint)(u.gen + 7);
				this.next_mark = 1;
				this.next_coref_mark = -1;
				this.late = new RefList<arr_tfs_entry>(40);

				var rels = mrs.RELS;
				int c_rels = rels.Count;
				Slot*[] rel_slots = new Slot*[c_rels];
				this.rgts = new TfsSlot[c_rels];
				this.var_slots = new Slot*[mrs.Variables.Count];

				Debug.Print("=== {0} ===", im.Grammar.Name);

				for (int i = 0; i < c_rels; i++)
				{
					rel_slots[i] = load_rel(i, rels[i]);
				}

				// patch vars

				for (int i = 0; i < c_rels; i++)
				{
					rgts[i].ix1 = write_rel_ix1(rel_slots[i]);
				}

				// write tfs
				Tfs tfs = null;

				for (int i = 0; i < c_rels; i++)
				{
					rgts[i] = new TfsSlot(tfs, rgts[i].ix1);
				}
			}

			Slot* load_rel(int i, Relation rel)
			{
				var t = get_rel_type(rel);

				if ((t.Expanded.flags & Tfs.Flags.RootCoverage) == 0)
					throw new Exception();

				Slot* ps = u._init_tfs_top_edge(t.Expanded);

				return ps;
			}

			int write_rel_ix1(Slot* ps)
			{
				int ix0 = late.AllocateNew();

				int m = 0;
				if ((ps->f & Edge.Flag.EtmNonBareType) != 0)
				{
					m = write_rel_mark(ps);
				}

				var arr = late.GetUntrimmed();
				arr[ix0].e_FlagsId = ps->f;
				arr[ix0].e_Mark = m;
				return ix0 + 1;
			}

			int write_rel_mark(Slot* ps)
			{
				int m = next_mark++;

			//		Slot* nh, nl;
			//pinfo* pihi = pbx + hi->tfsix, pilo = pbx + lo->tfsix;
			//int c = *(pfix - 1);
			//do
			//{
			//}
			//while (*++pfix != 0xFFFF);

			//	Slot* nps;
			//	ushort* pfix = *(ushort**)((long)u.rgpfix + ((int)ps->f << 3));
			//	int c = *(pfix - 1);
			//	do
			//	{
			//		if ((nps = u._source_fetch(ps, u.pbx + ps->tfsix, *pfix)) == null)
			//		{
			//			//if ((opts & Tfs.Flags.RootCoverage) == 0)
			//			//	c--;
			//			continue;
			//		}
			//		if (nps->gen == gen + g_nwf && (nps->f & Edge.Flag.EtmNonBareType) != 0)
			//		{
			//			Slot* ps_exp = _init_tfs_top_edge(_get_expand_tfs(nps->f));
			//			if (!_duplex_hilo(nps, ps_exp))
			//				return false;
			//		}
			//	}
			//	while (*++pfix != 0xFFFF);

				return m;
			}

	Type get_rel_type(Relation rel)
			{
				FeatMgr ftm = im.ftm;

				var rgfix = new int[rel.Count];
				int i = 0;
				for (; i < rgfix.Length; i++)
				{
					if ((rgfix[i] = ftm.GetFeatureIndex(rel[i].Arg)) == -1)
						throw new Exception(String.Format("MRS argument '{0}' is not valid for grammar '{1}'", rel[i].Arg, im.Grammar.Name));
				}

				if (!String.IsNullOrWhiteSpace(rel.PRED))
					arr.Append(ref rgfix, mrsm.ifeat_pred);
				if (!String.IsNullOrWhiteSpace(rel.CARG))
					arr.Append(ref rgfix, mrsm.ifeat_carg);

				var t = im.GetMaximalTypeForFeatures(rgfix);
				if (t == null)
					throw new Exception(String.Format("Grammar '{0}' has no type which is compatible with MRS features: {{ {1} }}",
						ftm.Grammar.Name,
						rgfix.Select(fix => ftm[fix].FEATURE).StringJoin(", ")));

				//Debug.Print("{0} - {{ {1} }}", t.Name, rgfix.Select(fix => ftm[fix].FEATURE).StringJoin(", "));

				return t;
			}
		}
	};
#endif
}